﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;

//using MSWord = Microsoft.Office.Interop.Word;


namespace BookWriter
{
    public enum FileType
    {
        folder,
        txt,
        rtf,
        txz,
        rtz,
    }

    public partial class Form1 : Form
    {
        //MSWord.Application wordApp;

        public string bookDir;
        public string bookName;
        public string title;

        private SearchDialog searchDialog = null;

        HashSet<TabPage> contentChangeList = new HashSet<TabPage>();
        List<string> recentBookList = new List<string>();


        public Font contentFont;
        public Color contentFontColor;
        public Color contentColor;
        public bool isConfigChange;



        public Form1()
        {
            InitializeComponent();
            title = Text;
        }

        private void LoadDirNode(TreeNodeCollection parent,string dir)
        {
            //if (dir[dir.Length - 1] == '\\') dir = dir.Substring(0, dir.Length - 1);
            parent.Clear();
            //处理目录
            var names = Directory.EnumerateDirectories(dir);
            foreach (var name in names)
            {
                FileDesc fd = new FileDesc();
                fd.type = FileType.folder;
                fd.filename = name;
                fd.name = Path.GetFileName(name);
                TreeNode node = new TreeNode();
                node.Text = fd.name;
                //node.SelectedImageKey = node.ImageKey = "folder";
                node.StateImageKey = "folder";
                node.Tag = fd;
                fd.node = node;
                parent.Add(node);
                //如果存在子目录，更新
                LoadDirNode(node.Nodes, name);
            }
            //处理txt文件
            names = Directory.EnumerateFiles(dir, "*.txt");
            foreach (var name in names)
            {
                FileDesc fd = new FileDesc();
                fd.type = FileType.txt;
                fd.name = Path.GetFileNameWithoutExtension(name);
                fd.filename = Path.GetDirectoryName(name) + "\\" + fd.name;
                TreeNode node = new TreeNode();
                node.Text = fd.name;
                //node.SelectedImageKey = node.ImageKey = "txt";
                node.StateImageKey = "txt";
                node.Tag = fd;
                fd.node = node;
                parent.Add(node);
            }
            //处理rtf文件
            names = Directory.EnumerateFiles(dir, "*.rtf");
            foreach (var name in names)
            {
                FileDesc fd = new FileDesc();
                fd.type = FileType.rtf;
                fd.name = Path.GetFileNameWithoutExtension(name);
                fd.filename = Path.GetDirectoryName(name) + "\\" + fd.name;
                TreeNode node = new TreeNode();
                node.Text = fd.name;
                //node.SelectedImageKey = node.ImageKey = "rtf";
                node.StateImageKey = "rtf";
                node.Tag = fd;
                fd.node = node;
                parent.Add(node);
            }
            //处理txz文件
            names = Directory.EnumerateFiles(dir, "*.txz");
            foreach (var name in names)
            {
                FileDesc fd = new FileDesc();
                fd.type = FileType.txz;
                fd.name = Path.GetFileNameWithoutExtension(name);
                fd.filename = Path.GetDirectoryName(name) + "\\" + fd.name;
                TreeNode node = new TreeNode();
                node.Text = fd.name;
                //node.SelectedImageKey = node.ImageKey = "txt";
                node.StateImageKey = "txz";
                node.Tag = fd;
                fd.node = node;
                parent.Add(node);
            }
            //处理rtf文件
            names = Directory.EnumerateFiles(dir, "*.rtz");
            foreach (var name in names)
            {
                FileDesc fd = new FileDesc();
                fd.type = FileType.rtz;
                fd.name = Path.GetFileNameWithoutExtension(name);
                fd.filename = Path.GetDirectoryName(name) + "\\" + fd.name;
                TreeNode node = new TreeNode();
                node.Text = fd.name;
                //node.SelectedImageKey = node.ImageKey = "rtf";
                node.StateImageKey = "rtz";
                node.Tag = fd;
                fd.node = node;
                parent.Add(node);
            }
            TreeNodeCollection p = parent;
        }

        private void onRecentMenuClick(object sender, EventArgs e)
        {
            LoadBook(((ToolStripItem)sender).Text);
        }

        private void refreshRecentMenu()
        {
            for (int i = 2; i < m_main_recent.DropDownItems.Count;i++ )
                m_main_recent.DropDownItems.RemoveAt(2);
            if(recentBookList.Count>0)
            {
                m_main_recent.Enabled=true;
                foreach(string file in recentBookList)
                {
                    var item=m_main_recent.DropDownItems.Add(file);
                    item.Click += onRecentMenuClick;
                }
            }
            else
                m_main_recent.Enabled = false;
        }

        /// <summary>
        /// 打开新的书籍
        /// </summary>
        /// <param name="dir"></param>
        public void LoadBook(string dir)
        {
           
            bool changed = false;
            //是否有未保存的
            foreach(TabPage page in tab_content.TabPages)
            {
                FileDesc fd = page.Tag as FileDesc;
                if(fd.node.TreeView==tv_charpter)
                if(contentChangeList.Contains(page))
                {
                    changed = true;
                    break;
                }
            }
            if (!changed || MessageBox.Show("原书籍还有未保存的章节，确认取消吗？", "确认", MessageBoxButtons.OKCancel) == DialogResult.OK)
            {
                //关闭旧的书籍
                for (int i = 0; i < tab_content.TabPages.Count;i++)
                {
                    TabPage page = tab_content.TabPages[i];
                    FileDesc fd = page.Tag as FileDesc;
                    if (fd.node.TreeView == tv_charpter)
                    {
                        contentChangeList.Remove(page);
                        tab_content.TabPages.RemoveAt(i);
                        fd.node = null;
                        i--;
                    }
                }
                //清除搜索列表
                if(searchDialog!=null)
                    searchDialog.Clear();

                //打开新的
                bookDir = dir;
                bookName = Path.GetFileName(dir);
                Text = title + "-" + bookName;
                LoadDirNode(tv_charpter.Nodes, bookDir);
                tv_charpter.Enabled = true;
            }

            recentBookList.Remove(dir);
            recentBookList.Insert(0, dir);
            isConfigChange = true;
            refreshRecentMenu();
        }


        private void ｍ_open_Click(object sender, EventArgs e)
        {
            if(folderBrowserDialog1.ShowDialog()==DialogResult.OK)
            {
                LoadBook(folderBrowserDialog1.SelectedPath);
            }
        }

        private TreeNode addNode(TreeNodeCollection parent, bool dir=false)
        {
            TreeNode node = new TreeNode();
            if (dir)
            {
                node.Text = "新目录";
                //node.SelectedImageKey = node.ImageKey = "folder";
                node.StateImageKey = "folder";
            }
            else
            {
                node.Text = "新节点";
                //node.SelectedImageKey = node.ImageKey = "txt";
                node.StateImageKey = "txt";
            }
            parent.Add(node);
            
            TreeNode pnode = node.Parent;
            FileDesc fd = new FileDesc();
            fd.name = node.Text;
            fd.type = dir?FileType.folder:FileType.txt;

            if(pnode!=null)
            { 
                FileDesc fd2=pnode.Tag as FileDesc;
                fd.filename = fd2.filename + "\\" + fd.name;
            }
            else
            {
                if (node.TreeView == tv_charpter)
                    fd.filename = bookDir + "\\" + fd.name;
                else
                    fd.filename = Directory.GetCurrentDirectory() + "\\material\\" + fd.name;
            }

            fd.node = node;
            node.Tag = fd;

            if (dir)
                Directory.CreateDirectory(fd.filename);
            else
                addContentPage(fd);
            node.BeginEdit();
            return node;
        }

        private void m_dir_addsub_Click(object sender, EventArgs e)
        {
            bool dir = (string)((ToolStripMenuItem)sender).Tag == "dir";
            if (tab_dir.SelectedIndex == 0)
            {
                if(tv_charpter.SelectedNode!=null)
                {
                    if (tv_charpter.SelectedNode.StateImageKey=="folder")
                        tv_charpter.SelectedNode = addNode(tv_charpter.SelectedNode.Nodes, dir);
                }
                else
                {
                    tv_charpter.SelectedNode =  addNode(tv_charpter.Nodes,dir);
                }
            }
            else //if (tab_dir.SelectedIndex == 1)
            {
                if (tv_material.SelectedNode != null)
                {
                    if (tv_material.SelectedNode.StateImageKey == "folder")
                        tv_material.SelectedNode = addNode(tv_material.SelectedNode.Nodes,dir);
                }
                else
                {
                    tv_material.SelectedNode = addNode(tv_material.Nodes,dir);
                }
            }
        }

 
        private void m_dir_add_Click(object sender, EventArgs e)
        {
            bool dir = (string)((ToolStripMenuItem)sender).Tag == "dir";
            if (tab_dir.SelectedIndex == 0)
            {
                if (tv_charpter.SelectedNode != null && tv_charpter.SelectedNode.Parent != null)
                {
                    tv_charpter.SelectedNode = addNode(tv_charpter.SelectedNode.Parent.Nodes, dir);
                }
                else
                {
                    tv_charpter.SelectedNode = addNode(tv_charpter.Nodes, dir);
                }
            }
            else //if (tab_dir.SelectedIndex == 1)
            {
                if (tv_material.SelectedNode !=null && tv_material.SelectedNode.Parent != null)
                {
                    tv_material.SelectedNode = addNode(tv_material.SelectedNode.Parent.Nodes, dir);
                }
                else
                {
                    tv_material.SelectedNode = addNode(tv_material.Nodes, dir);
                }
            }
        }


        public void addContentPage(FileDesc fd)
        {
            TabPage page = new TabPage(fd.name);
            tab_content.TabPages.Add(page);
            DocumentEditor tb = new DocumentEditor();
            tb.richTextBox.Font = contentFont;
            tb.richTextBox.BackColor = contentColor;
            tb.Tag = fd;
            tb.Init(fd);
            page.Controls.Add(tb);
            tb.Dock = DockStyle.Fill;

            //if (wordApp == null)
            //    wordApp = new MSWord.Application();

            fd.page = page;
            fd.textBox = tb;
            fd.textBox.Tag = fd;
            fd.textBox.onContentChange += onContentChange;
            fd.textBox.onContentTypeChanged += onContentTypeChanged;
            fd.textBox.onContentSaved += onContentSave;
            page.Tag = fd;

            fd.textBox.LoadFile();
            
            tab_content.SelectedTab = page;
        }

        private void tv_charpter_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
        {
            FileDesc fd = e.Node.Tag as FileDesc;
            string new_name = e.Label ?? e.Node.Text;
            new_name = GetVoidName(e.Node.Parent == null ? e.Node.TreeView.Nodes : e.Node.Parent.Nodes, new_name,e.Node);
            if(fd.name!=new_name)
            {
                if (fd.page != null)
                    fd.page.Text = new_name + (contentChangeList.Contains(fd.page) ? "*" : "");
                string new_filename = fd.filename.Substring(0, fd.filename.Length - fd.name.Length) + new_name;
                fd.name = new_name;
                if(fd.type==FileType.folder)
                {
                    if (System.IO.Directory.Exists(fd.filename))
                        Directory.Move(fd.filename, new_filename);
                }
                else if (fd.type == FileType.txt )
                {
                    if(File.Exists(fd.filename + ".txt"))
                    File.Move(fd.filename + ".txt", new_filename + ".txt");
                }
                else if (fd.type == FileType.rtf)
                {
                    if (File.Exists(fd.filename + ".rtf"))
                        File.Move(fd.filename + ".rtf", new_filename + ".rtf");
                }
                else if (File.Exists(fd.filename))
                    File.Move(fd.filename , new_filename);

                fd.filename = new_filename;
            }
            e.CancelEdit = true;
            e.Node.Text = new_name;
        }

        private void m_font_Click(object sender, EventArgs e)
        {
            fontDialog1.Font = contentFont;
            if (fontDialog1.ShowDialog() == DialogResult.OK)
            {
                contentFont = fontDialog1.Font;
                refreshContentStyle();
                isConfigChange = true;
            }
        }

        private void m_bgcolor_Click(object sender, EventArgs e)
        {
            colorDialog1.Color = contentColor;
            if (colorDialog1.ShowDialog() == DialogResult.OK)
            {
                contentColor = colorDialog1.Color;
                refreshContentStyle();
                isConfigChange = true;
            }
        }


        private void m_fontcolor_Click(object sender, EventArgs e)
        {
            colorDialog1.Color = contentFontColor;
            if (colorDialog1.ShowDialog() == DialogResult.OK)
            {
                contentFontColor = colorDialog1.Color;
                refreshContentStyle();
                isConfigChange = true;
            }
        }

        private void refreshContentStyle()
        {
            foreach(TabPage page in tab_content.TabPages)
            {
                DocumentEditor tb = page.Controls[0] as DocumentEditor;
                tb.richTextBox.Font = contentFont;
                tb.richTextBox.BackColor = contentColor;
                tb.richTextBox.ForeColor = contentFontColor;
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            if(File.Exists("BookWriter.ini"))
            {
                IniFile ini = new IniFile("BookWriter.ini", Encoding.Default);
                contentFont = new Font(
                    ini.get("font", "family", "宋体"),  //family
                    ini.getFloat("font", "size", 12f),
                    (FontStyle)Enum.Parse(typeof(FontStyle), ini.get("font", "style", "Regular")),
                    GraphicsUnit.Point);  //fontstyle
                contentFontColor = Color.FromArgb(ini.getInt("font", "color", SystemColors.WindowText.ToArgb()));
                contentColor = Color.FromArgb(ini.getInt("font", "bgcolor", SystemColors.Window.ToArgb()));

                var map=ini.getSectionDict("recent");
                if (map!=null)
                for(int i=0;i<8;i++)
                    if (map.ContainsKey(i.ToString()))
                        recentBookList.Add(map[i.ToString()]);

                isConfigChange = false;
            }
            else
            {
                contentFont = RichTextBox.DefaultFont;
                contentFontColor = SystemColors.Window;
                contentColor = SystemColors.WindowText;

                isConfigChange = true;
            }
            refreshRecentMenu();
            //读取资料
            if (Directory.Exists("material"))
                LoadDirNode(tv_material.Nodes, Directory.GetCurrentDirectory()+"\\material");

            string txt = EncryptUtils.AESEncrypt("张斌", "zhangbin");
            Console.Out.WriteLine(txt);
            txt = EncryptUtils.AESDecrypt(txt, "zhangbin");
            Console.Out.WriteLine(txt);

        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (isConfigChange)
            {
                IniFile ini = new IniFile();
                ini.set("font", "family", contentFont.Name);
                ini.set("font", "size", contentFont.SizeInPoints.ToString());
                ini.set("font", "style", contentFont.Style.ToString());
                ini.set("font", "color", contentFontColor.ToArgb().ToString());
                ini.set("font", "bgcolor", contentColor.ToArgb().ToString());
                for (int i = 0; i < recentBookList.Count; i++)
                    ini.set("recent", i.ToString(), recentBookList[i]);
                ini.saveFile("BookWriter.ini");
            }
        }

        private void m_quit_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if(contentChangeList.Count>0)
            {
                if (MessageBox.Show("还有文件未保存，确认退出吗？", "确认", MessageBoxButtons.OKCancel) == DialogResult.Cancel)
                    e.Cancel = true;
            }
        }

        private void savePage(TabPage page,bool force=false)
        {
            if (page != null && (force||contentChangeList.Contains(page)))
            {
                contentChangeList.Remove(page);
                page.Text = page.Text.Substring(0, page.Text.Length - 1);
                FileDesc fd = page.Tag as FileDesc;
                if (fd.textBox.notEmpty())
                    fd.textBox.SaveFile();
                else
                {
                    if (File.Exists(fd.filename + ".txt"))
                        File.Delete(fd.filename + ".txt");
                    if (File.Exists(fd.filename + ".rtf"))
                        File.Delete(fd.filename + ".rtf");
                    if (File.Exists(fd.filename + ".txz"))
                        File.Delete(fd.filename + ".txz");
                    if (File.Exists(fd.filename + ".rtz"))
                        File.Delete(fd.filename + ".rtz");
                }
            }
        }

        private void m_save_Click(object sender, EventArgs e)
        {
            savePage(tab_content.SelectedTab);
        }

        private void m_save_all_Click(object sender, EventArgs e)
        {
            foreach (TabPage page in tab_content.TabPages)
                savePage(page);
        }

        private void onContentChange(DocumentEditor sender)
        {
            FileDesc fd = sender.Tag as FileDesc;
            if (contentChangeList.Add(fd.page))
                fd.page.Text = fd.page.Text + "*";
        }

        private void onContentSave(DocumentEditor sender)
        {
            savePage(sender.Parent as TabPage);
        }

        private void onContentTypeChanged(DocumentEditor sender, FileType old_type)
        {
            FileDesc fd = sender.Tag as FileDesc;
            string old_filename = fd.filename + "." + old_type.ToString();
            if (File.Exists(old_filename))
                File.Delete(old_filename);
            fd.node.StateImageKey = fd.type.ToString();
            savePage(sender.Parent as TabPage,true);
        }

        private void tv_charpter_DoubleClick(object sender, EventArgs e)
        {
            TreeNode node = ((TreeView)sender).SelectedNode;
            if(node!=null)
            {
                FileDesc fd = node.Tag as FileDesc;
                if(fd.type!=FileType.folder&&fd.page==null)
                {
                    addContentPage(fd);
                }
            }
        }


        private void tab_content_DoubleClick(object sender, EventArgs e)
        {
            if(tab_content.SelectedTab!=null)
            {
                if (contentChangeList.Contains(tab_content.SelectedTab))
                    if (MessageBox.Show("该文档还未保存，确认关闭吗？", "确认", MessageBoxButtons.OKCancel) == DialogResult.Cancel)
                        return;
                FileDesc fd = tab_content.SelectedTab.Tag as FileDesc;
                fd.page = null;
                fd.textBox = null;
                contentChangeList.Remove(tab_content.SelectedTab);
                tab_content.TabPages.RemoveAt(tab_content.SelectedIndex);
            }
        }

        private void m_dir_Opening(object sender, CancelEventArgs e)
        {
            TreeView tv = tab_dir.SelectedIndex==0?tv_charpter:tv_material;
            m_dir_addsub.Enabled = m_dir_addsub_dir.Enabled = (tv.SelectedNode != null && tv.SelectedNode.StateImageKey == "folder");
            m_dir_remove.Enabled = m_dir_rename.Enabled = m_dir_explorer.Enabled = (tv.SelectedNode != null);
        }

        private void tv_charpter_MouseClick(object sender, MouseEventArgs e)
        {
            TreeView tv = sender as TreeView;
            tv.SelectedNode = tv.GetNodeAt(e.X,e.Y);
        }

        private void m_dir_explorer_Click(object sender, EventArgs e)
        {
            TreeView tv = tab_dir.SelectedIndex == 0 ? tv_charpter : tv_material;
            if(tv.SelectedNode!=null)
            {
                FileDesc fd = tv.SelectedNode.Tag as FileDesc;
                Shell32.ExplorerFile(fd.FullName);
            }
        }

        private void m_dir_search_Click(object sender, EventArgs e)
        {
            TreeView tv = tab_dir.SelectedIndex == 0 ? tv_charpter : tv_material;
            if (searchDialog == null) searchDialog = new SearchDialog();
            searchDialog.Clear();
            if(tv.SelectedNode!=null)
                searchDialog.Show(this, tv.SelectedNode);
            else
                searchDialog.Show(this, tv.Nodes);
        }

        public void MarkChange(FileDesc fd)
        {
            if(fd.page!=null)
            {
                if(!contentChangeList.Contains(fd.page))
                {
                    contentChangeList.Add(fd.page);
                    fd.page.Text += "*";
                }
            }
        }

        private void m_main_recent_clear_Click(object sender, EventArgs e)
        {
            recentBookList.Clear();
            isConfigChange = true;
            refreshRecentMenu();
        }


        private void tv_material_ItemDrag(object sender, ItemDragEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                DoDragDrop(e.Item, DragDropEffects.Move);

        }

        public string checkExistsFile(string filename)
        {
            while (File.Exists(filename))
            {
                int end = filename.LastIndexOf('.');
                if (end == -1) end = filename.Length;
                int start = end - 1;
                if (start >= 3 && filename[start] == ')')
                {
                    int i = start - 1;
                    while (filename[i] >= '0' && filename[i] <= '9') i--;
                    if (i >= 0 && i + 1 < start && filename[i] == '(')
                    {
                        int no = 0;
                        if (int.TryParse(filename.Substring(i + 1, start - i - 1), out no))
                        {
                            filename = filename.Substring(0, i + 1) + (no + 1) + ")" + filename.Substring(end);
                            continue;
                        }
                    }
                }
                filename = filename.Substring(0, end) + "(1)" + filename.Substring(end);
            }
            return filename;
        }


        private string GetVoidName(TreeNodeCollection nodes,string name,TreeNode exceptNode=null)
        {
            List<string> list = new List<string>();
            foreach (TreeNode node in nodes)
                if (node != exceptNode)
                    list.Add(node.Text);
            return StringUtils.GetVoidName(list, name);
        }

        private void MoveFile(FileDesc fd,FileDesc fd_parent){
            string target;
            if (fd_parent == null)
                target = tab_dir.SelectedIndex == 0 ? bookDir : Directory.GetCurrentDirectory() + "\\material";
            else
                target = fd_parent.filename;

            string old_filename = fd.FullName;
            fd.name= GetVoidName(fd_parent==null?fd.node.TreeView.Nodes:fd_parent.node.Nodes,fd.name);
            fd.filename = target + "\\" + fd.name;
            Directory.Move(old_filename, fd.FullName);
        }

        private void tv_material_DragDrop(object sender, DragEventArgs e)
        {
            TreeView tv = sender as TreeView;
            Point targetPoint = tv.PointToClient(new Point(e.X, e.Y));
            TreeNode targetNode = tv.GetNodeAt(targetPoint);
            TreeNode draggedNode = (TreeNode)e.Data.GetData(typeof(TreeNode));

            if (!draggedNode.Equals(targetNode) && e.Effect == e.AllowedEffect) 
            {

                draggedNode.Remove();
                if (targetNode == null)
                    tv.Nodes.Add(draggedNode);
                else
                {
                    targetNode.Nodes.Add(draggedNode);
                    targetNode.Expand();
                }
                MoveFile(draggedNode.Tag as FileDesc, targetNode == null ? null : (FileDesc)targetNode.Tag);
            }

        }


        private void tv_material_DragOver(object sender, DragEventArgs e)
        {
            TreeView tv = sender as TreeView;
            Point targetPoint = tv.PointToClient(new Point(e.X, e.Y));
            TreeNode targetNode = tv.GetNodeAt(targetPoint);
            if(targetNode==null)
            {
                e.Effect = e.AllowedEffect;
                tv.SelectedNode = targetNode;
                return;
            }
            else if (targetNode.StateImageKey == "folder" )
            {
                TreeNode draggedNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
                if (draggedNode.Parent != targetNode)
                {
                    e.Effect = e.AllowedEffect;
                    tv.SelectedNode = targetNode;
                    return;
                }
            }
            e.Effect = DragDropEffects.None;
            tv.SelectedNode = null;

        }

    }
}
